package com.mezz.bones.im.logic.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.mezz.bones.framework.core.helper.SnowflakeIdGeneratorHelper;
import com.mezz.bones.framework.core.strategy.StrategyContext;
import com.mezz.bones.im.common.enums.SessionTypeEnum;
import com.mezz.bones.im.common.exception.ImBaseException;
import com.mezz.bones.im.common.message.body.in.*;
import com.mezz.bones.im.common.message.body.out.ConversationRecordRes;
import com.mezz.bones.im.common.message.body.out.ConversationPrivateInfoRes;
import com.mezz.bones.im.common.message.body.out.MessageHasBeenReadRes;
import com.mezz.bones.im.common.domain.msg.ConversationIdBo;
import com.mezz.bones.im.common.domain.user.ImUserBaseInfo;
import com.mezz.bones.im.common.result.ImResultCode;
import com.mezz.bones.im.common.util.MessageUtil;
import com.mezz.bones.im.logic.domain.bo.ChatConversationInitBo;
import com.mezz.bones.im.logic.domain.bo.ChatConversationLatestDataBo;
import com.mezz.bones.im.logic.domain.context.PrivateMessageCreateCtx;
import com.mezz.bones.im.logic.domain.entity.ChatConversation;
import com.mezz.bones.im.logic.domain.entity.ChatGroupInfo;
import com.mezz.bones.im.logic.domain.entity.ChatUser;
import com.mezz.bones.im.logic.repository.IChatConversationRepository;
import com.mezz.bones.im.logic.repository.IChatGroupInfoRepository;
import com.mezz.bones.im.logic.repository.IChatUserRemarkRepository;
import com.mezz.bones.im.logic.service.IChatConversationService;
import com.mezz.bones.im.logic.service.IChatUserService;
import com.mezz.bones.im.logic.startegy.interfaces.IMessageContentTypeStrategy;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: zhuang
 * @Date: 2024/12/25 13:59
 * @Description: TODO
 * @Version: 1.0
 **/

@Service
@Slf4j
@AllArgsConstructor
public class ChatConversationServiceImpl implements IChatConversationService {

    private final IChatConversationRepository chatConversationRepository;

    private final SnowflakeIdGeneratorHelper snowflakeIdGeneratorHelper;

    private final StrategyContext strategyContext;

    private final IChatUserService chatUserService;

    private final RedissonClient redissonClient;
    private final IChatUserRemarkRepository userRemarkRepository;
    private final IChatGroupInfoRepository groupInfoRepository;

    @Override
    public void flushChatConversationByPrivateMessage(PrivateMessageCreateCtx context) {

        PrivateMessageCreateReq body = context.getBody();

        //初始化聊天会话
        ChatConversation userConversation = conversationGetOrInit(context.getUserConversationInitBo());
        //对端会话数据
        ChatConversation peerConversation = conversationGetOrInit(context.getPeerConversationInitBo());

        context.setUserConversation(userConversation);
        context.setPeerConversation(peerConversation);

        //更新聊天会话-最后一次聊天数据
        ChatConversationLatestDataBo conversationLatestDataBo = ChatConversationLatestDataBo.builder()
                .lastMsgKey(context.getHeader().getTraceId())
                .lastMsgSeq(body.getMsgSeq())
                .lastMsgContent(formatMsgContent(body.getContentType(), body.getContent()))
                .lastMsgTime(context.getNow())
                .cursorId(snowflakeIdGeneratorHelper.nextId())
                .build();

        //更新发送端会话信息
        conversationLatestDataBo.setId(userConversation.getId());
        updateChatConversation(conversationLatestDataBo);

        if(body.getSync2Peer() == 1){

            //更新接收端会话信息
            conversationLatestDataBo.setId(peerConversation.getId());
            updateChatConversation(conversationLatestDataBo);

            //对端用户 未读数+1
            incrPeerConversationUnreadNum(peerConversation);
        }
    }

    /**
     * 格式化消息内容(会话列表中展示)
     *
     * @param contentType
     * @param content
     * @return
     */
    @Override
    public String formatMsgContent(String contentType, JSONObject content) {

        IMessageContentTypeStrategy strategy = strategyContext.getStrategy(IMessageContentTypeStrategy.class, contentType);
        return strategy.formatContentDisplay(content);
    }

    @Override
    public ConversationPrivateInfoRes getPrivateConversationInfo(ConversationPrivateInfoReq req) {

        String userId = req.getUserId();
        String peerId = req.getPeerId();
        String relationId = req.getRelationId();

        ImUserBaseInfo userBaseInfo = chatUserService.getUserBaseInfo(userId);
        if (userBaseInfo == null) {
            throw new ImBaseException(ImResultCode.DATA_NOT_FOUND, StrUtil.format("用户不存在={}", userId));
        }

        ImUserBaseInfo peerBaseInfo = chatUserService.getUserBaseInfo(peerId);
        if (peerBaseInfo == null) {
            throw new ImBaseException(ImResultCode.DATA_NOT_FOUND, StrUtil.format("用户不存在={}", peerId));
        }

        ChatConversation conversation = conversationGetOrInit(
                ChatConversationInitBo.builder()
                        .now(new Date())
                        .userId(userId)
                        .userType(userBaseInfo.getUserType())
                        .peerId(peerId)
                        .peerType(peerBaseInfo.getUserType())
                        .relationId(relationId)
                        .relationType(MessageUtil.relationType(userBaseInfo.getUserType(),peerBaseInfo.getUserType(),relationId))
                        .sessionTypeEnum(SessionTypeEnum.PRIVATE)
                        .build()
        );

        return buildConversationInfoRes(conversation);

    }

    /**
     * 构建会话聊天数据
     *
     * @param conversation
     * @return
     */
    private ConversationPrivateInfoRes buildConversationInfoRes(ChatConversation conversation) {
        ConversationPrivateInfoRes res = new ConversationPrivateInfoRes();

        res.setConversationId(conversation.getId());
        res.setSessionType(conversation.getSessionType());
        res.setUserId(conversation.getUserId());
        res.setUserType(conversation.getUserType());
        res.setPeerId(conversation.getPeerId());
        res.setPeerType(conversation.getPeerType());

        //会话所属者用户信息
        ChatUser userInfo = chatUserService.getByUserId(conversation.getUserId());
        //对端用户信息
        ChatUser peerInfo = chatUserService.getByUserId(conversation.getPeerId());

        res.setUserNickname(userInfo.getNickname());
        res.setUserAvatar(userInfo.getAvatar());

        res.setPeerNickname(peerInfo.getNickname());
        res.setPeerAvatar(peerInfo.getAvatar());

        //给对端用户的备注昵称
        res.setPeerRemarkNickname(peerInfo.getNickname());
        String peerRemarkNickname = userRemarkRepository.getPeerRemarkNickname(userInfo.getId(), peerInfo.getId());
        if(StrUtil.isNotBlank(peerRemarkNickname)){
            res.setPeerRemarkNickname(peerRemarkNickname);
        }

        res.setLastMsgSeq(conversation.getLastMsgSeq());
        res.setLastPeerReadSeq(conversation.getLastPeerReadSeq());
        res.setUnreadCount(conversation.getUnreadCount());
        res.setCreateTime(conversation.getCreateTime());

        return res;
    }

    @Override
    public List<ConversationRecordRes> getUserConversationRecord(ConversationRecordReq req) {

        List<ChatConversation> records = chatConversationRepository.findUserConversationPage(req);

        //对端用户信息
        Set<String> peerIds = new HashSet<>();
        Set<String> groupIds = new HashSet<>();

        for (ChatConversation record : records) {

            if (record.getSessionType().equals(1)) {
                peerIds.add(record.getPeerId());
            } else {
                groupIds.add(record.getPeerId());
            }
        }

        Map<String, ChatUser> peerUserMap = chatUserService.getByUserIds(peerIds).stream().collect(Collectors.toMap(
                ChatUser::getUserId,
                item -> item
        ));

        Map<String, ChatGroupInfo> groupInfoMap = groupInfoRepository.listByIds(groupIds).stream().collect(Collectors.toMap(
                ChatGroupInfo::getId,
                item -> item
        ));

        //对端用户备注信息
        Map<String, String> peersRemarkNicknameMap = userRemarkRepository.findPeersRemarkNickname(req.getUserId(), new ArrayList<>(peerIds));

        //TODO 对端群组信息

        //可能是私聊用户 可能是群组信息

        return records.stream().map(item -> {
            ConversationRecordRes res = new ConversationRecordRes();
            res.setConversationId(item.getId());
            res.setSessionType(item.getSessionType());
            res.setPeerId(item.getPeerId());
            res.setPeerType(item.getPeerType());

            // 设置对端信息
            if (item.getSessionType().equals(1)) {
                //用户数据
                ChatUser peerUserInfo = peerUserMap.get(item.getPeerId());
                if (peerUserInfo != null) {

                    res.setPeerNickname(peerUserInfo.getNickname());
                    res.setPeerAvatar(peerUserInfo.getAvatar());
                    //查看是否有备注信息
                    res.setPeerRemarkName("");
                    String peerRemarkName = peersRemarkNicknameMap.get(peerUserInfo.getUserId());
                    if(StrUtil.isNotBlank(peerRemarkName)){
                        res.setPeerRemarkName(peerRemarkName);
                    }

                }
            } else {
                //群组数据

                ChatGroupInfo chatGroupInfo = groupInfoMap.get(item.getPeerId());
                if (chatGroupInfo != null) {
                    res.setPeerNickname(chatGroupInfo.getGroupName());
                    res.setPeerAvatar(chatGroupInfo.getAvatarUrl());
                    //可设置群备注 GroupMember
                    res.setPeerRemarkName("");
                }
            }

            res.setLastMsgContent(item.getLastMsgContent());
            res.setLastMsgTime(item.getLastMsgTime());
            res.setUnreadCount(item.getUnreadCount());
            res.setTopFlag(item.getTopFlag());
            res.setCursorId(item.getCursorId());
            return res;
        }).collect(Collectors.toList());

    }

    @Override
    public void userRemoveConversation(ConversationRemoveReq req) {

        ChatConversation chatConversation = chatConversationRepository.getById(req.getConversationId());
        if (chatConversation == null) {
            throw new ImBaseException(ImResultCode.DATA_NOT_FOUND, "会话数据不存在");
        }

        if (!chatConversation.getUserId().equals(req.getUserId())) {
            throw new ImBaseException(ImResultCode.ILLEGAL_OPERATION);
        }

        chatConversationRepository.removeConversation(req);
    }

    @Override
    public void userTopConversation(ConversationTopReq req) {

        ChatConversation chatConversation = chatConversationRepository.getById(req.getConversationId());
        if (chatConversation == null) {
            throw new ImBaseException(ImResultCode.DATA_NOT_FOUND, "会话数据不存在");
        }

        if (!chatConversation.getUserId().equals(req.getUserId())) {
            throw new ImBaseException(ImResultCode.ILLEGAL_OPERATION);
        }

        chatConversationRepository.topConversation(req);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MessageHasBeenReadRes messageHasBeenRead(MessageHasBeenReadReq req) {

        ChatConversation conversation = chatConversationRepository.getById(req.getConversationId());
        if (conversation == null) {
            throw new ImBaseException(ImResultCode.DATA_NOT_FOUND);
        }

        if (!conversation.getUserId().equals(req.getUserId())) {
            throw new ImBaseException(ImResultCode.ILLEGAL_OPERATION);
        }

        //前端发起请求的时候  应该判断 当前会话的 lastUserReadSeq < req.hasReadMsgSeq 符合此条件才需要请求这个 已读操作

        //查询出对端的会话ID 然后更新对端会话ID中的 lastPeerReadSeq
        ConversationIdBo conversationIdBo = MessageUtil.peerConversationIdBoInfo(conversation.getUserId(), conversation.getPeerId(), conversation.getRelationId(), conversation.getRelationType());

        //对端会话信息
        ChatConversation peerConversation = chatConversationRepository.getByUserIdAndPeerId(conversationIdBo.getUserId(), conversationIdBo.getPeerId());

        //更新对端会话的 lastPeerReadSeq字段数据
        chatConversationRepository.updateLastPeerReadSeq(peerConversation.getId(), req.getHasReadMsgSeq());

        //更新该会话的 lastUserReadSeq字段数据
        chatConversationRepository.updateLastUserReadSeq(conversation.getId(), req.getHasReadMsgSeq());

        MessageHasBeenReadRes res = new MessageHasBeenReadRes();
        res.setUserId(peerConversation.getUserId());
        res.setConversationId(peerConversation.getId());
        res.setHasReadMsgSeq(req.getHasReadMsgSeq());
        return res;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void clearUnreadCountConversation(ConversationClearUnreadCountReq req) {

        ChatConversation conversation = chatConversationRepository.getById(req.getConversationId());
        if (conversation == null) {
            throw new ImBaseException(ImResultCode.DATA_NOT_FOUND);
        }

        if (!conversation.getUserId().equals(req.getUserId())) {
            throw new ImBaseException(ImResultCode.ILLEGAL_OPERATION);
        }

        //重置会话模型中的 unReadNum
        chatConversationRepository.clearConversationUnreadNum(req.getConversationId());
    }

    private void incrPeerConversationUnreadNum(ChatConversation peerConversation) {

        chatConversationRepository.updateConversationUnreadNum(peerConversation.getId(), 1);
    }

    private void updateChatConversation(ChatConversationLatestDataBo bo) {

        chatConversationRepository.updateChatConversation(bo);
    }

    public ChatConversation conversationGetOrInit(ChatConversationInitBo chatConversationInitBo) {

        RLock rLock = redissonClient.getLock("conversation:init:" + chatConversationInitBo.getUserId() + ":" + chatConversationInitBo.getPeerId());

        try {
            // 尝试加锁
            boolean lock = false;
            try {
                lock = rLock.tryLock(5000,15000, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                throw new ImBaseException(ImResultCode.TRY_LOCK_ERROR);
            }

            if (!lock) {
                throw new ImBaseException(ImResultCode.TRY_LOCK_ERROR);
            }

            ChatConversation userConversation = chatConversationRepository.getByUserIdAndPeerId(chatConversationInitBo.getUserId(), chatConversationInitBo.getPeerId());
            if (userConversation == null) {
                userConversation = chatConversationRepository.initPrivateConversation(chatConversationInitBo);
            }
            return userConversation;

        } finally {
            // 如果未注册事务同步或发生其他情况，确保锁被释放
            if (rLock.isHeldByCurrentThread()) {
                rLock.unlock();
            }
        }


    }
}
